using LispLib;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;
using Noein.Control;
using Noein.GeneticAlgorithm.New;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;

namespace Noein.Checkpoint
{
    /// <summary>
    /// A framework for organizing initialization and setup code of different checkpoints.
    /// </summary>
    public abstract class CPFramework
    {
        protected Device device;
        protected List<Object> objects = new List<Object>();
        protected List<Object> renderables = new List<Object>();
        protected Camera cam;
        public Dialog Ui;

        public CPFramework(Device device, Dialog ui, Camera camera)
        {
            this.device = device;
            Ui = ui;
            cam = camera;
        }

        public virtual void Update(double t, float dt)
        {
            foreach (Object obj in objects)
            {
                obj.Update(device, dt);
            }
        }

        public virtual void Render()
        {
            foreach (Object obj in renderables)
            {
                obj.Render(device);
            }
        }

        protected void AddObject(Object obj)
        {
            objects.Add(obj);
            if (obj.Visual != null && obj.Spatial != null)
            {
                renderables.Add(obj);
            }
        }
    }

    /// <summary>
    /// simple math function controlled motion
    /// </summary>
    public class CP1 : CPFramework
    {
        public CP1(Device d, Dialog ui, Camera cam)
            : base(d, ui, cam) 
        {
            Object tiger = new Object();
            tiger.Visual = new FrameworkMeshVisual(new FrameworkMesh(d, @"Resource\tiger.x"));

            ScriptController c1 = new ScriptController(@"(set (x (+ x (* dt 5))))", "Spatial.X");
            ScriptController c2 = new ScriptController(@"(set (x (+ x (* dt 5))))", "Spatial.Y");
            ScriptController c3 = new ScriptController(@"(set (x (* 2 dt)))", "Spatial.RotateX");
            c1.Pause = true;
            c2.Pause = true;
            c3.Pause = true;

            //target.Spatial.rotateAxis(new Vector3(0, 1, 0), 2f * elapsedTime);     

            c1.AttachTo(tiger);
            c2.AttachTo(tiger);
            c3.AttachTo(tiger);

            AddObject(tiger);

            Button b = ui.AddButton(0, "run/reset", 50, 0, 100, 22);
            b.Click += delegate(object obj, EventArgs e)
            {
                c1.Pause = !c1.Pause;
                c2.Pause = c1.Pause;
                c3.Pause = c1.Pause;
            };
        }
    }

    /// <summary>
    /// keyframe animation
    /// </summary>
    public class CP2 : CPFramework
    {
        public CP2(Device d, Dialog ui, Camera cam)
            : base(d, ui, cam)
        {
            // make a tiger model
            Object tiger = new Object();
            tiger.Visual = new FrameworkMeshVisual(new FrameworkMesh(device, @"Resource\tiger.x"));
            
            // attach a key frame controller to it
            Controller c = new KeyframeController(@"Resource\keyframe-input.txt");
            c.AttachTo(tiger);
            c.Loop = true;

            // add the key frame driven tiger to the system
            AddObject(tiger);

            // add a button that control the play/pause of the key frame controller
            int y = 10;
            Button playAnimation = ui.AddButton(0, "Pause/Play", 35, y += 24, 125, 22);
            playAnimation.Click += 
                delegate(object sender, EventArgs e)
                {
                    c.Pause = !c.Pause;
                };
        }

        public override void Update(double t, float dt)
        {
            base.Update(t, dt);

            // make camera follow the tiger
            cam.SetViewParameters(new Vector3(0, 0, -10), objects[0].Spatial.Position);
        }
    }

    /// <summary>
    /// billiards
    /// </summary>
    public class CP3 : CPFramework
    {
        private ParticleController physics = new ParticleController();
        private CollisionController collisions = new CollisionController();
        private List<Object> balls = new List<Object>();
        private List<Object> table = new List<Object>();

        private Physics.Friction friction;

        private bool run = false;
        public bool Run
        {
            get { return run; }
            set 
            { 
                run = value; 
                physics.Pause = !value; 
                collisions.Pause = !value;
            }
        }

        private float ballRadius = .08f;
        private float tableWidth = 2.438f;
        private float tableLength = 1.298f;
        private float tableHeight = .1f;
        private float cushionThickness = .1f;

        public CP3(Device d, Dialog ui, Camera cam)
            : base(d, ui, cam)
        {
            #region UI setup

            int y = 0;
            int id = 0;

            // config file drop box
            ComboBox cb = ui.AddComboBox(id++, 50, y += 24, 100, 22);
            cb.AddItem("config 1", @"..\..\Resource\billiards-input01.txt");
            cb.AddItem("config 2", @"..\..\Resource\billiards-input02.txt");
            cb.Click += new EventHandler(cb_Click);

            // static friction constant slider
            Slider usSlider = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "static friction", 50, y += 24, 100, 22);
            usSlider.ValueChanged += 
                delegate(object sender, EventArgs e)
                {
                    friction.CoefOfStaticFriction = ((Slider)sender).Value * .01f;
                };

            //// kinetic friction constant slider
            Slider ukSlider = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "kinetic friction", 50, y += 24, 100, 22);
            ukSlider.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    friction.CoefOfKineticFriction = ((Slider)sender).Value * .01f;
                };

            // ball mass slider
            Slider massSlider = ui.AddSlider(id++, 50, y += 24, 100, 22);
            massSlider.ValueChanged += new EventHandler(massSlider_ValueChanged);
            ui.AddStatic(id++, "ball mass", 50, y += 24, 100, 22);

            // impact coefficient of restitution slider
            Slider corSlider = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "c.o.r", 50, y += 24, 100, 22);
            corSlider.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    collisions.COR = ((Slider)sender).Value * .01f;
                };
                
            // integration drop box
            ComboBox integrationMethods = ui.AddComboBox(id++, 50, y += 24, 100, 22);
            integrationMethods.AddItem("Euler", new Math.Integrate(Math.NumericalIntegration.Euler));
            integrationMethods.AddItem("Mid Point", new Math.Integrate(Math.NumericalIntegration.Midpoint));
            integrationMethods.AddItem("RK4", new Math.Integrate(Math.NumericalIntegration.RungeKutta4));
            integrationMethods.Click += new EventHandler(integrationMethods_Click);

            // run/reset button
            Button b = ui.AddButton(id++, "run/reset", 50, y += 24, 100, 22);
            b.Click += new EventHandler(b_Click);

            #endregion

            #region ball setup

            balls = new List<Object>();
            for (int i = 0; i < 4; i++)
            {
                Object ball = new Object();
                ball.Spatial = new Spatial();
                ball.Visual = new MeshVisual(Mesh.Sphere(d, ballRadius, 10, 10), new Material());
                ball.Physical = new Physics.Physical(ball.Spatial);
                ball.Physical.Mass = massSlider.Value * .01f;
                ball.BoundingVolume = new Physics.Collision.Sphere(ballRadius);
                physics.AttachTo(ball);
                collisions.AttachTo(ball);
                balls.Add(ball);

                AddObject(ball);
            }
            ReadFile();

            #endregion

            #region table setup

            table = new List<Object>();

            Material surfaceM = new Material();
            surfaceM.Diffuse = System.Drawing.Color.Green;
            Material cushionM = new Material();
            cushionM.Diffuse = System.Drawing.Color.Brown;

            Object surface = new Object();
            surface.Spatial = new Spatial();
            surface.Visual = new MeshVisual(Mesh.Box(d, tableWidth, tableHeight, tableLength), surfaceM);
            surface.Physical = new Physics.Physical(surface.Spatial);
            surface.BoundingVolume = new Physics.Collision.AABB(tableWidth / 2f, tableHeight / 2f, tableLength / 2f);
            surface.Physical.Mass = massSlider.Value * .01f;
            surface.Spatial.TeleportTo(new Vector3(0, tableHeight * -.5f, 0));

            Object cushionL = new Object();
            cushionL.Spatial = new Spatial();
            cushionL.Visual = new MeshVisual(Mesh.Box(d, cushionThickness, cushionThickness, tableLength), cushionM);
            cushionL.Physical = new Physics.Physical(cushionL.Spatial);
            cushionL.BoundingVolume = new Physics.Collision.AABB(cushionThickness / 2f, cushionThickness / 2f, tableLength / 2);
            cushionL.Physical.Mass = massSlider.Value * .01f;
            cushionL.Spatial.TeleportTo(new Vector3((tableWidth + cushionThickness) * -.5f, tableHeight * .5f, 0f));

            Object cushionR = new Object();
            cushionR.Spatial = new Spatial();
            cushionR.Visual = new MeshVisual(Mesh.Box(d, cushionThickness, cushionThickness, tableLength), cushionM);
            cushionR.Physical = new Physics.Physical(cushionR.Spatial);
            cushionR.BoundingVolume = new Physics.Collision.AABB(cushionThickness / 2f, cushionThickness / 2f, tableLength / 2);
            cushionR.Physical.Mass = massSlider.Value * .01f;
            cushionR.Spatial.TeleportTo(new Vector3((tableWidth + cushionThickness) * .5f, tableHeight * .5f, 0f));

            Object cushionF = new Object();
            cushionF.Spatial = new Spatial();
            cushionF.Visual = new MeshVisual(Mesh.Box(d, tableWidth, cushionThickness, cushionThickness), cushionM);
            cushionF.Physical = new Physics.Physical(cushionF.Spatial);
            cushionF.BoundingVolume = new Physics.Collision.AABB(tableWidth / 2f, cushionThickness / 2f, cushionThickness / 2f);
            cushionF.Physical.Mass = massSlider.Value * .01f;
            cushionF.Spatial.TeleportTo(new Vector3(0f, tableHeight * .5f, (tableLength + cushionThickness) * .5f));

            Object cushionB = new Object();
            cushionB.Spatial = new Spatial();
            cushionB.Visual = new MeshVisual(Mesh.Box(d, tableWidth, cushionThickness, cushionThickness), cushionM);
            cushionB.Physical = new Physics.Physical(cushionB.Spatial);
            cushionB.BoundingVolume = new Physics.Collision.AABB(tableWidth / 2f, cushionThickness / 2f, cushionThickness / 2f);
            cushionB.Physical.Mass = massSlider.Value * .01f;
            cushionB.Spatial.TeleportTo(new Vector3(0f, tableHeight * .5f, (tableLength + cushionThickness) * -.5f));

            collisions.AttachTo(surface);
            collisions.AttachTo(cushionL);
            collisions.AttachTo(cushionR);
            collisions.AttachTo(cushionF);
            collisions.AttachTo(cushionB);

            table.Add(surface);
            table.Add(cushionL);
            table.Add(cushionR);
            table.Add(cushionF);
            table.Add(cushionB);

            AddObject(surface);
            AddObject(cushionL);
            AddObject(cushionR);
            AddObject(cushionF);
            AddObject(cushionB);
            
            #endregion

            physics.Pause = !run;
            collisions.Pause = !run;

            friction = new Physics.Friction(usSlider.Value * .01f, ukSlider.Value * .01f);
            physics.AddForce(friction);
            //cam.SetViewParameters(new Vector3(0, 10, -10), Vector3.Empty);
        }

        public override void Update(double t, float dt)
        {
            base.Update(t, dt);
            cam.SetViewParameters(new Vector3(0, 3, -3), Vector3.Empty);
        }

        /// <summary>
        /// Load billiard configuration from file.
        /// </summary>
        private void ReadFile()
        {
            // get configuration file name from UI
            string name = (string)Ui.GetComboBox(0).GetSelectedData();
            if (!File.Exists(name))
                throw new Exception("Pool simulation config file not found!");

            using (StreamReader sr = File.OpenText(name))
            {
                string line;
                int lineCount = 0;
                while ((line = sr.ReadLine()) != null)
                {
                    // split each line considering space as separator
                    Regex o = new Regex(@"[\s]+");
                    string[] entries = o.Split(line);

                    // HACK we going to make a lot assumptions about file format here
                    if (lineCount < 4)
                    {
                        // first 4 lines are regular balls balls: R G B x y z
                        Material material = new Material();
                        material.DiffuseColor = new ColorValue(
                            (float)Convert.ToDouble(entries[0]),
                            (float)Convert.ToDouble(entries[1]),
                            (float)Convert.ToDouble(entries[2]));
                        ((MeshVisual)balls[lineCount].Visual).Material = material;

                        Vector3 position = new Vector3(
                            (float)Convert.ToDouble(entries[3]),
                            (float)Convert.ToDouble(entries[4]),
                            (float)Convert.ToDouble(entries[5]));
                        balls[lineCount].Spatial.TeleportTo(position);

                        balls[lineCount].Physical = new Physics.Physical(balls[lineCount].Spatial);
                    }
                    else if (lineCount == 4)
                    {
                        // 5th line is about physics of cue ball: p_x p_y p_z L_x L_y Lz, p = momentum, L = angular momentum
                        balls[0].Physical.Momentum = new Vector3(
                            (float)Convert.ToDouble(entries[0]),
                            (float)Convert.ToDouble(entries[1]),
                            (float)Convert.ToDouble(entries[2]));

                        //    angularMomentum = new Vector3(
                        //        (float)Convert.ToDouble(entries[3]),
                        //        (float)Convert.ToDouble(entries[4]),
                        //        (float)Convert.ToDouble(entries[5]));
                        //}
                    }
                    lineCount++;
                }
                sr.Close();
            }

            Run = false;
        }

        #region UI events handler
        private void cb_Click(object sender, EventArgs e)
        {
            ReadFile();
        }

        void massSlider_ValueChanged(object sender, EventArgs e)
        {
            foreach (Object ball in balls)
            {
                ball.Physical.Mass = ((Slider)sender).Value * .01f;
                // minimum mass cap
                if (ball.Physical.Mass < 0.01f)
                {
                    ball.Physical.Mass = 0.01f;
                }
            }
        }

        void integrationMethods_Click(object sender, EventArgs e)
        {
            physics.Integrator = (Math.Integrate)(((ComboBox)sender).GetSelectedData());
        }

        private void b_Click(object sender, EventArgs e)
        {
            if (Run)
            {
                ReadFile();
            }
            else
            {
                Run = !Run;
            }
        }

        #endregion
    }

    /// <summary>
    /// particle system
    /// </summary>
    public class CP4 : CPFramework
    {
        public CP4(Device device, Dialog ui, Camera cam)
            : base(device, ui, cam)
        {
            // make a pool of red sphere particles
            Object obj = new ParticleList();
            Material m = new Material();
            m.Diffuse = Color.Red;
            obj.Visual = new MeshVisual(Mesh.Sphere(device, .1f, 20, 20), m);

            // attach a particle controller to it
            Physics.Physical p = new Physics.Physical(1, null, new Vector3(0, 1, 0), Vector3.Empty);
            ParticlesController c = new ParticlesController(2, 60, 40f, .1f, 0, p);
            c.AttachTo(obj);

            // start up particle system
            c.Pause = false;

            // set up a key frame controller to move the emitter around
            Controller c2 = new KeyframeController(@"Resource\keyframe-input.txt");
            c2.AttachTo(obj);
            c2.Pause = true;
            c2.Loop = false;

            // add the emitter to the system
            AddObject(obj);

            // make camera look at the emitter
            cam.SetViewParameters(obj.Spatial.Position + new Vector3(0, 0, -10), obj.Spatial.Position);

            #region UI

            int y = 0;
            int id = 0;

            // emitter spread radius
            Slider spreadRadius = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "cone radius", 50, y += 24, 100, 22);
            spreadRadius.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c.SpreadRadius = ((Slider)sender).Value * 0.01f;
                };

            // emitter cone angle
            Slider spreadAngle = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "cone angle", 50, y += 24, 100, 22);
            spreadAngle.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c.SpreadAngle = ((Slider)sender).Value * 1.79f * (float)System.Math.PI / 180f;
                };

            // emitter rate
            Slider emitRate = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "emit rate", 50, y += 24, 100, 22);
            emitRate.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c.Rate = ((Slider)sender).Value;
                };

            // particle life time
            Slider lifeTime = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "particle life", 50, y += 24, 100, 22);
            lifeTime.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c.LifeTime = ((Slider)sender).Value * .1f;
                };

            // randomness
            
            // animate by keyframe
            Button animate = ui.AddButton(id++, "animate", 50, y += 24, 100, 22);
            animate.Click +=
                delegate(object sender, EventArgs e)
                {
                    if (c2.Pause)
                        c2.Pause = false;
                    else
                        c2.Reset(obj);
                };

            #endregion
        }

        public override void Update(double t, float dt)
        {
            base.Update(t, dt);

            // make camera track the emitter
            cam.SetViewParameters(cam.EyeLocation, objects[0].Spatial.Position);
        }
    }

    /// <summary>
    /// boids/flocking
    /// </summary>
    public class CP5 : CPFramework
    {
        public CP5(Device device, Dialog ui, Camera cam)
            : base(device, ui, cam)
        {
            ParticleList obj = new ParticleList();
            Material m = new Material();
            m.Diffuse = Color.WhiteSmoke;
            obj.Visual = new MeshVisual(Mesh.Sphere(device, .1f, 20, 20), m);
            obj.Spatial = new Spatial();
            obj.Physical = new Physics.Physical(obj.Spatial);

            FlockController c1 = new FlockController();
            c1.NBoids = 10;
            c1.CollisionWeight = 1;
            c1.CenteringWeight = 1;
            c1.VelocityMatchingWeight = 1;
            c1.FollowWeight = 1;
            c1.Range = 2;
            c1.Angle = 100;
            c1.AttachTo(obj);
            c1.Pause = false;

            Controller c2 = new KeyframeController(@"Resource\keyframe-input.txt");
            c2.AttachTo(obj);
            c2.Pause = true;
            c2.Loop = true;

            AddObject(obj);

            #region UI

            int y = 0;
            int id = 0;

            // emitter spread radius
            Slider nboids = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "# boids", 50, y += 24, 100, 22);
            nboids.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c1.NBoids = ((Slider)sender).Value;
                };

            Slider collisionW = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "collision W", 50, y += 24, 100, 22);
            collisionW.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c1.CollisionWeight = ((Slider)sender).Value;
                };

            Slider centerW = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "center W", 50, y += 24, 100, 22);
            centerW.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c1.CenteringWeight = ((Slider)sender).Value;
                };

            Slider velocityW = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "velocity W", 50, y += 24, 100, 22);
            velocityW.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c1.VelocityMatchingWeight = ((Slider)sender).Value;
                };

            Slider followW = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "follow W", 50, y += 24, 100, 22);
            followW.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c1.FollowWeight = ((Slider)sender).Value;
                };

            Slider range = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "range", 50, y += 24, 100, 22);
            range.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c1.Range = ((Slider)sender).Value * .05f;
                };

            Slider acceleration = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "Force", 50, y += 24, 100, 22);
            acceleration.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    c1.Acceleration = ((Slider)sender).Value;
                };

            Button start = ui.AddButton(id++, "start/reset", 50, y += 24, 100, 22);
            start.Click +=
                delegate(object sender, EventArgs e)
                {
                    c2.Pause = !c2.Pause;
                };

            #endregion
        }

        public override void Update(double t, float dt)
        {
            base.Update(t, dt);

            // make camera track the flock leader
            cam.SetViewParameters(cam.EyeLocation, objects[0].Spatial.Position);
        }
    }

    /// <summary>
    /// test
    /// </summary>
    public class FinalProject : CPFramework
    {
    //    public struct Output
    //    {
    //        public float averageSpeed;
    //        public float energyUsed;
    //    }

    //    // TODO need implementation
    //    //private GeneticAlgorithm.Problem<Object, Output> problem;
    //    //private GeneticAlgorithm.FitnessFunction<Output> TestFitness;
    //    //private GeneticAlgorithm.Selection<LispLib.Interpreter.Pair, Object, Output> selection;

        //private GASystem system;

        private float ks;
        private float kd;
        private float km;
        private bool gravity;
        


        public FinalProject(Device device, Dialog ui, Camera cam)
            : base(device, ui, cam)
        {

            Material m = new Material();
            m.Diffuse = Color.WhiteSmoke;
            Visual mass = new MeshVisual(Mesh.Sphere(device, .1f, 20, 20), m);

            SpringMass obj = new SpringMass(@"Resource\SpringMass.txt", mass, 40, 10, 40, false);
            obj.Visual = mass;
            obj.Spatial = new Spatial();
            obj.Spatial.TeleportTo(new Vector3(0, 3, 0));

            AddObject(obj);

            #region UI

            int y = 0;
            int id = 0;

            Slider spring = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "spring const", 50, y += 24, 100, 22);
            spring.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    ks = ((Slider)sender).Value;
                };

            Slider damper = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "damper const", 50, y += 24, 100, 22);
            damper.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    kd = ((Slider)sender).Value;
                };

            Slider muscle = ui.AddSlider(id++, 50, y += 24, 100, 22);
            ui.AddStatic(id++, "muscle const", 50, y += 24, 100, 22);
            muscle.ValueChanged +=
                delegate(object sender, EventArgs e)
                {
                    km = ((Slider)sender).Value;
                };

            Checkbox gravityCb = ui.AddCheckBox(id++, "gravity", 50, y += 24, 100, 22, false);
            gravityCb.Changed +=
                delegate(object sender, EventArgs e)
                {
                    gravity = gravityCb.IsChecked;
                };


            Checkbox moveCb = ui.AddCheckBox(id++, "walk", 50, y += 24, 100, 22, false);
            moveCb.Changed +=
                delegate(object sender, EventArgs e)
                {
                    ((SpringMass)objects[0]).move = moveCb.IsChecked;
                };

            Button reset = ui.AddButton(id++, "reset", 50, y += 24, 100, 22);
            reset.Click +=
                delegate(object sender, EventArgs e)
                {
                    objects.Clear();
                    renderables.Clear();

                    SpringMass newObj = new SpringMass(@"Resource\SpringMass.txt", mass, ks, kd, km, gravity);
                    newObj.Visual = mass;
                    newObj.Spatial = new Spatial();
                    newObj.Spatial.TeleportTo(new Vector3(0, 3, 0));

                    AddObject(newObj);
                };

            #endregion

            #region Junk
            // how to set up spring
            //Object obj1 = new Object();
            //Object obj2 = new Object();

            //Material m = new Material();
            //m.Diffuse = Color.WhiteSmoke;
            //Visual v = new MeshVisual(Mesh.Sphere(device, .1f, 20, 20), m);
            //obj1.Visual = v;
            //obj2.Visual = v;

            //obj1.Spatial = new Spatial(new Vector3(-1, 0, 0), Matrix.Identity);
            //obj2.Spatial = new Spatial(new Vector3(1, 0, 0), Matrix.Identity);

            //obj1.Physical = new Physics.Physical(obj1.Spatial);
            //obj2.Physical = new Physics.Physical(obj2.Spatial);


            //ParticleController pc = new ParticleController();
            //pc.AddForce(new Physics.SpringForce(obj1, obj2, 1, .5f, 1));

            //pc.AttachTo(obj1);
            //pc.AttachTo(obj2);
            //pc.Pause = false;

            //AddObject(obj1);
            //AddObject(obj2);

            //List<Individual> initialPopulation = new List<Individual>();
            //initialPopulation.Add(new Individual(Interpreter.Parse("(+ 1 2)")));
            //initialPopulation.Add(new Individual(Interpreter.Parse("(- 1 2)")));
            //initialPopulation.Add(new Individual(Interpreter.Parse("(* 1 2)")));
            //initialPopulation.Add(new Individual(Interpreter.Parse("(/ 1 2)")));

            //Material m = new Material();
            //m.Diffuse = Color.WhiteSmoke;
            //Visual v = new MeshVisual(Mesh.Sphere(device, .1f, 20, 20), m);

            //Object obj1 = new Object();
            //obj1.Visual = v;
            //obj1.Spatial = new Spatial();
            //Object obj2 = new Object();
            //obj2.Visual = v;
            //obj2.Spatial = new Spatial();
            //Object obj3 = new Object();
            //obj3.Visual = v;
            //obj3.Spatial = new Spatial();
            //Object obj4 = new Object();
            //obj4.Visual = v;
            //obj4.Spatial = new Spatial();
            //List<Object> objs = new List<Object>();
            //objs.Add(obj1);
            //objs.Add(obj2);
            //objs.Add(obj3);
            //objs.Add(obj4);
            //Problem p = new Problem(objs);

            //system = new GASystem(
            //    4, 4, initialPopulation,
            //    new RandomSelector(),
            //    new RandomSelector(),
            //    new NodeSwap(new RandomSelector()),
            //    new Dummy(new RandomSelector()),
            //    p,
            //    5);

            #endregion
        }

        public override void Update(double t, float dt)
        {
            base.Update(t, dt);
            cam.SetViewParameters(new Vector3(0, 0, -30), new Vector3(0, 0, 0));
        }

        //public override void Update(double t, float dt)
        //{
        //    base.Update(t, dt);
        //    //system.Update(dt);
        //}
            
    //           //        // TODO individual implementation
    //        //        // need to make phenotype actual mass spring creature object instead of same as genotype

    //        //        // TODO problem implementation
    //        //        // need problem placeholder: takes parsed tree, create creature, run simulation, return result
    //        //        //CP3.Problem problem = new CP3.Problem();

    //        //        // TODO selection, need to modify final.select to extentiate framework's select function

    //        //        //Selection<LispLib.Interpreter.Pair, LispLib.Interpreter.Pair, float[]> selection =
    //        //        //    new Selection<LispLib.Interpreter.Pair, LispLib.Interpreter.Pair, float[]>(
    //        //        //    .75f, .001f, 10, CP3.ParseTree.Crossover.A, CP3.ParseTree.Mutation.A);

    //        //        //Procedure<LispLib.Interpreter.Pair, LispLib.Interpreter.Pair, float[]> procedure =
    //        //        //    new Procedure<LispLib.Interpreter.Pair, LispLib.Interpreter.Pair, float[]>(problem, selection, CP3.Fitness.A);

    //        //        //procedure.Run(new CP3.ParseTree.Population(), CP1.Terminate<LispLib.Interpreter.Pair, LispLib.Interpreter.Pair>.A, true);

    //    }

    //    public override void Update(double t, float dt)
    //    {
    //        base.Update(t, dt);

          
    //        int id = 0;

    //        // tracks fitness over generations
    //        float greatestFit = 0;
    //        List<float> fitnesses = new List<float>();

    //        while (!TestTerminate(population))
    //        {
    //            // create new generation
    //            List<Individual<TGenotype, TPhenotype>> newGeneration = selection.NextGeneration(population);

    //            // compute the fitness of each individual in the new generation
    //            foreach (Individual<TGenotype, TPhenotype> individual in newGeneration)
    //            {
    //                //individual.Id[0] = id++;
    //                individual.Fitness = TestFitness(problem.Evaluate(individual.Phenotype));
    //            }



    //            // add new generation to the population
    //            population.AddGeneration(newGeneration);

    //            // remove the expired and the least fit
    //            //population.Cull();
    //        }

    //    }
    }

    public class GAProject : CPFramework
    {
        private GASystem gaSys;

        public GAProject(Device device, Dialog ui, Camera cam)
            : base(device, ui, cam)
        {


            // init pop
            List<Individual> population = new List<Individual>();

            List<Interpreter.Pair> l1 = new List<Interpreter.Pair>();
            l1.Add(Interpreter.Parse("(set (x (* 20 (sin t))))"));
            l1.Add(Interpreter.Parse("(set (x (* 20 (sin t))))"));
            l1.Add(Interpreter.Parse("(set (x (* 20 (sin t))))"));

            List<Interpreter.Pair> l2 = new List<Interpreter.Pair>();
            l2.Add(Interpreter.Parse("(set (x (* 1 3)))"));
            l2.Add(Interpreter.Parse("(set (x (* 1 3)))"));
            l2.Add(Interpreter.Parse("(set (x (* 1 3)))"));
            
            List<Interpreter.Pair> l3 = new List<Interpreter.Pair>();
            l3.Add(Interpreter.Parse("(set (x 6))"));
            l3.Add(Interpreter.Parse("(set (x 7))"));
            l3.Add(Interpreter.Parse("(set (x 8))"));

            List<Interpreter.Pair> l4 = new List<Interpreter.Pair>();
            l4.Add(Interpreter.Parse("(set (x 9))"));
            l4.Add(Interpreter.Parse("(set (x 10))"));
            l4.Add(Interpreter.Parse("(set (x 11))"));

            population.Add(new Individual(l1));
            population.Add(new Individual(l2));
            population.Add(new Individual(l3));
            population.Add(new Individual(l4));


            // init problem
            Material m = new Material();
            m.Diffuse = Color.White;
            Visual mass = new MeshVisual(Mesh.Sphere(device, .1f, 20, 20), m);

            SpringMass o1 = new SpringMass(@"Resource\Triangle.txt", mass, 40, 10, 40, true);
            o1.Spatial = new Spatial();
            o1.Spatial.TeleportTo(new Vector3(0, 3, 0));
            o1.Visual = mass;
            o1.move = true;
            SpringMass o2 = new SpringMass(@"Resource\Triangle.txt", mass, 40, 10, 40, true);
            o2.Spatial = new Spatial();
            o2.Spatial.TeleportTo(new Vector3(0, 3, 0));
            o2.Visual = mass;
            o2.move = true;
            SpringMass o3 = new SpringMass(@"Resource\Triangle.txt", mass, 40, 10, 40, true);
            o3.Spatial = new Spatial();
            o3.Spatial.TeleportTo(new Vector3(0, 3, 0));
            o3.Visual = mass;
            o3.move = true;
            SpringMass o4 = new SpringMass(@"Resource\Triangle.txt", mass, 40, 10, 40, true);
            o4.Spatial = new Spatial();
            o4.Spatial.TeleportTo(new Vector3(0, 3, 0));
            o4.Visual = mass;
            o4.move = true;
            List<Object> objs = new List<Object>();
            objs.Add(o1);
            objs.Add(o2);
            objs.Add(o3);
            objs.Add(o4);

            AddObject(o1);
            AddObject(o2);
            AddObject(o3);
            AddObject(o4);

            Problem problem = new Problem(objs);

            // init system
            gaSys = new GASystem(
                4, 4, 
                population, 
                new TopFit(), new TopFit(), 
                new NodeSwap(new RandomSelector()), null, problem, 5);

        }

        public override void Update(double t, float dt)
        {
            base.Update(t, dt);
            gaSys.Update(dt);

            cam.SetViewParameters(new Vector3(0, 0, -30), new Vector3(0, 0, 0));
        }
    }

}